#include "XYmdm.h"
#include "config.h"
#include "util.h"


#define PRTCOM(x) x;

extern void InitElrUser();
extern int elr_usb_close( void );
extern unsigned int ElrUDCWrite(char *pTxBuffer, unsigned int BufLen);
extern int WriteImage(int from);
extern int ReadImage(int to);
extern int SplitRam(int addr, int nSize);
extern int WriteIPL(int addr);
extern int WriteSPL(int addr);
extern int WriteLogo(int ptr, int size);
extern int FormatDocSafe();
extern void hw_reset();
void msleep(unsigned int ms);

#define MobileBits  16
#define MobileWidth     240
#define MobileHeight    320

#define KERN_BUF_BASE 0xA0000000
#define KENN_BUF_OFF  0x00020000
#define LOGO_BUF_BASE 0xA0000000
#define LOGO_BUF_OFF  0x00000000
#define LOGO_RAW_BASE 0xA0800000

#define LOGO_RAW_SIZE (MobileWidth * MobileHeight * MobileBits)

#define CMD_LENGTH    0x08
#define CMD_SERIAL    0x06
#define CMD_COUNT     0x06

static int g_CrcCode = 0;
static int g_Filelen = 0;

#define Color32To19(color32) (unsigned int)(/*0x00040000 |*/ (((color32) & 0xFC0000) >> 6) | \
                                (((color32) & 0x00FC00) >> 4) | \
                                (((color32) & 0x0000FC) >> 2))

#define Color32To16(color32) (unsigned short)((((color32) & 0xF80000) >> 8) | \
                                (((color32) & 0x00FC00) >> 5) | \
                                (((color32) & 0x0000F8) >> 3))

typedef struct __attribute__ ((packed)) _tagBITMAPINFOHEADER {
    unsigned int biSize;
    int biWidth;
    int biHeight;
    unsigned short biPlanes;
    unsigned short biBitCount;
    unsigned int biCompression;
    unsigned int biSizeImage;
    int biXPelsPerMeter;
    int biYPelsPerMeter;
    unsigned int biClrUsed;
    unsigned int biClrImportant;
}   BITMAPINFOHEADER;


typedef struct __attribute__ ((packed)) _tagBITMAPFILEHEADER {
     unsigned short bfType;
     unsigned int bfSize;
     unsigned short bfReserved1;
     unsigned short bfReserved2;
     unsigned int bfOffBits;
}   BITMAPFILEHEADER;

char kCmd[CMD_COUNT][CMD_LENGTH + 1] = { "KERNEL00\0",//DOWNLOAD KERNEL IMAGE TO RAM
                                         "KERNEL01\0",//DOWNLOAD KERNEL IMAGE TO DOC
                                         "KERNEL02\0",//DOWNLOAD LOADER IMAGE TO DOC
                                         "KERNEL03\0",//DOWNLOAD LOGO PICTURE TO DOC
                                         "KERNEL04\0",//FORMAT DOC
                                         "KERNEL05\0",
                                        };

int kBufBase[CMD_COUNT] = { RAM_START, RAM_START, RAM_START, LOGO_BUF_BASE, RAM_START, 0};

int kBufOffset[CMD_COUNT] = { KENN_BUF_OFF, KENN_BUF_OFF, UDL_RAM_OFFSET, LOGO_BUF_OFF, 0, 0};

void CircumgyratePic(char *pSrc, int nBmpBufSize, int nByteCount, unsigned short *target)
{
    short *pPixels = (unsigned short *)target;
    unsigned int Value = 0;
    int nBytesPerLine = MobileWidth * nByteCount;
    char *pLine;
    int i, j;

    PRTCOM(printk("width %d,nByteCount %d\n",MobileBits,nByteCount));

    if (nByteCount == 4) {
        memcpy(pPixels, pSrc, nBmpBufSize);
    }
    else {
        for (i = 0; i < MobileHeight; i++) {
            pLine = pSrc + (MobileHeight - i - 1) * nBytesPerLine;
            for (j = 0; j < MobileWidth; j++, pLine += 3) {
                Value = 0;
                memcpy(&Value, pLine, 3);
                *pPixels++ = Color32To16(Value);
            }
        }
    }
    return;
}

int BmpToRaw(int RawDataPtr)
{
    BITMAPFILEHEADER *pFileHeader;
    BITMAPINFOHEADER *pInfoHeader;
    int nBmpBufSize, nByteCount;
    char *pBmp, *ptrRawData = (char*)(RawDataPtr);

    pFileHeader = (BITMAPFILEHEADER*)ptrRawData;


    if (pFileHeader->bfType != ((short)('M' << 8) | 'B')) {
        PRTCOM(printk("Cannot read picture info from file!\n"));
        return 0;
    }

    pInfoHeader = (BITMAPINFOHEADER*)(ptrRawData + sizeof(BITMAPFILEHEADER));

    PRTCOM(printk("biBitCount %d\n", pInfoHeader->biBitCount));

    if (pInfoHeader->biBitCount != 24) {
        PRTCOM(printk("Doesn't support the color format of the file!\n"));
        return 0;
    }

    if (pInfoHeader->biWidth != MobileWidth && pInfoHeader->biHeight != MobileHeight) {
        PRTCOM(printk("The width of picture read from file is not correct!\n"));
        return 0;
    }

    pBmp = ptrRawData + pFileHeader->bfOffBits;

    nByteCount = pInfoHeader->biBitCount / 8;
    nBmpBufSize = MobileWidth * MobileHeight * nByteCount;
    PRTCOM(printk("size %d\n",nBmpBufSize));

    CircumgyratePic(pBmp, nBmpBufSize, nByteCount, (unsigned short *)LOGO_RAW_BASE);

    return LOGO_RAW_BASE;

}

int ReceiveFile(char *bufbase ,int offset, int *filesize)
{
    char *pData;
    int pos = 0, err = 0;
    unsigned int bRead;
    char *kBase = 0;//Memory address base


    XYM_PKG XymPkg;
    P_XYM_PKG pXPacket = &XymPkg;
    pData = ((P_BLK_STR)pXPacket->PacketData)->Data;

    *filesize = 0;

    XYmodemInit();//Init XYmodem protocol
    RECV_INIT(YMDM);

    pXPacket->PacketStatus = PK_NONE;
    pXPacket->PacketDataLen = 0;
    pXPacket->ActionStatus = AC_NONE;

    do {
        Read((unsigned char*)pXPacket, sizeof(XYM_PKG), &bRead);
        if ( PK_VALIDDATA == pXPacket->PacketStatus ) {
//            PRTCOM(printk("PK_VALIDDATA pXPacket->PacketDataLen %d\n", pXPacket->PacketDataLen));
            if (0 != kBase) {
                memcpy((void*)(kBase + pos), (void*)pData, pXPacket->PacketDataLen);
                pos += pXPacket->PacketDataLen;
//                PRTCOM(printk("kBase + pos = %08x\r", kBase + pos));
                pXPacket->ActionStatus = AC_ACCEPT;
            }
            else {
                err = XmErrSys;
                goto RECV_ERR;
            }

        }
        else if ( PK_FILEINFO == pXPacket->PacketStatus ) {
        	if (g_CrcCode == 0 && g_Filelen == 0) {
	        	memcpy(&g_CrcCode, pData, 4);
	        	memcpy(&g_Filelen, pData + 4, 4);
	        	PRTCOM(printk("crcCode [%d] filelen [%d]\n", g_CrcCode, g_Filelen));
	        }

//            PRTCOM(printk("PK_FILEINFO pXPacket->PacketDataLen %d\n", pXPacket->PacketDataLen));
            if (0 != kBase) {
                *filesize = pos;
                kBase = 0;
                pos = 0;
            }
	        if( pData[0] == '\0' )	{/* last file */
	            RECV_ACCEPT();
	            goto RECV_ERR;
	        }

            kBase = (char*)(bufbase + offset);
//            PRTCOM(printk("Kernel Buffer Base %08x Postion %08x\n", kBase, pos));
            pXPacket->ActionStatus = AC_YMD1STPK;
        }
        else if ( PK_CANCEL == pXPacket->PacketStatus ) {
            err = 0;
            goto RECV_ERR;
        }
    }while(1);

RECV_ERR:
    if(err) {
        Cancel();
    }
    if (0 != kBase) {
        kBase = 0;
        pos = 0;
    }
    if (pXPacket) {
        pXPacket = 0;
    }
    return err;
}

extern int MyStrCmp(const char *s1, const char *s2, int maxlen);
extern unsigned int ElrUDCWrite(char *pTxBuffer, unsigned int BufLen);
extern unsigned int ElrUDCRead(char *pRxBuffer, unsigned int Bufflen);
#define SHOWVERSION "\n\tElastos2.0 Bootloader\n\tVersion:"BOOTLOADVERSION", Time Stamp:"COMPILE_TIME_STAMP"\n"
int WaitForCmd(int waitsec)
{
    char rBuf[128];
    unsigned int ret, idx = 0, cmdidx = 0;
    ElrUDCWrite(SHOWVERSION, sizeof(SHOWVERSION));
    ElrUDCWrite("**Waiting remote host send command\n", 36);
    while(1) {
        ret = ElrUDCRead(rBuf+idx, 1);
        if (ret) {
            //ElrUDCWrite(rBuf+idx, 1);
            idx++;
        }
        if (idx) {
            if (idx <= CMD_SERIAL) {
                if (MyStrCmp((const char *)rBuf, (const char *)kCmd[0], idx)) {
                    PRTCOM(printk("Expect Download Kernel Image Command Serials\n"));
                }
                else {
                    PRTCOM(printk("Unexpect Cmd, idx %02d %s\n", idx, rBuf));
                    idx = 0;
                }
            }
            else if (idx >= CMD_LENGTH) {
                for (cmdidx = 0; cmdidx < CMD_COUNT; cmdidx++) {
                    if (MyStrCmp((const char *)rBuf, (const char *)kCmd[cmdidx], CMD_LENGTH)) {
                        PRTCOM(printk("Expect Download Command : %s\n", kCmd[cmdidx]));
                        break;
                    }
                }

                if (cmdidx >= CMD_COUNT) {
                    PRTCOM(printk("Unkonwn Received Command %s\n", rBuf));
                    return -1;
                }

                return cmdidx;
            }
        }
    }
    return -1;
}

int UsbDownloadImageEx()
{
	int CmdCode, filesize, err = 0, retVal = 0;
	PRTCOM(printk("Elastos20 usb loader initialize ...\n"));

    InitElrUser();

    msleep(1000);

    CmdCode = WaitForCmd(0);

    if (-1 == CmdCode) {
        PRTCOM(printk("Unexpect Command Received!\n"));
        return 0;//
    }

    PRTCOM(printk("Expect Command Received!\n"));
    PRTCOM(printk("Start Receive file [0x%08x]...\n", kBufBase[CmdCode] + kBufOffset[CmdCode]));
    err = ReceiveFile((char*)kBufBase[CmdCode], kBufOffset[CmdCode], &filesize);
    PRTCOM(printk("Kernel Image Receive "));

    if (err) {
        PRTCOM(printk("Failed!\n"));
        return 0;//Receive Failed
    }

    PRTCOM(printk("Succeeded!\n"));

    switch(CmdCode) {
        case 0x00://DOWNLOAD KERNEL IMAGE TO RAM
            msleep(2000);
            break;

        case 0x01://DOWNLOAD KERNEL IMAGE TO DOC
            PRTCOM(printk("Write Kernel Image ... "));
            if (WriteImage(KERNEL_RAM_START) ) {
				ElrUDCWrite("Burn Image Into Doc Failed!\n", 29);
                PRTCOM(printk("Failed..........\n"));
            }
            else {
                PRTCOM(printk("Completed!\n"));
				int ret = ReadImage(KERNEL_RAM_START);
				if (!ret) {
					int crcCode = CalCrc((char*)KERNEL_RAM_START, g_Filelen);
					PRTCOM(printk("Crc after read is %d!\n", crcCode));
					if (crcCode == g_CrcCode) {
						ElrUDCWrite("CRC Check Successful!\n",23);
						PRTCOM(printk("CRC Check Success!\n"));
					} else {
						ElrUDCWrite("CRC Check Failed!\n",19);
						PRTCOM(printk("CRC Check Failed!\n"));
					}
				}
            }
            break;

        case 0x02://DOWNLOAD LOADER IMAGE TO DOC
            if (filesize > 0 ) {
                retVal = SplitRam((RAM_START + UDL_RAM_OFFSET), filesize);
                if ( retVal != 0) {
                	if( WriteSPL(retVal) ) {
                		PRTCOM(printk("Write SPL Failed!\n"));
                		return 0;
                	}
                	else {
                		PRTCOM(printk("Write SPL Successful!\n"));
                        if(WriteIPL(RAM_START + UDL_RAM_OFFSET)) {
                    	    PRTCOM(printk("Write IPL Failed!\n"));
                		    return 0;
                	    }
                	    else {
                    	    PRTCOM(printk("Write IPL Successful!\n"));
                    	    hw_reset();
                        }
                   }
                }
                else {
                	PRTCOM(printk("Update Loader Failed\n"));
                }
            } else {
                PRTCOM(printk("Download Loader Failed"));
            }
            break;

        case 0x03://DOWNLOAD LOGO PICTURE TO DOC
            retVal = BmpToRaw(LOGO_BUF_BASE + LOGO_BUF_OFF);
            if (retVal) {
                PRTCOM(printk("retVal Addr %08x\n", retVal));
                retVal = WriteLogo(retVal, filesize);
    			if (retVal) {
                    PRTCOM(printk("Write logo error:%d\n", retVal));
                }
                else {
                    PRTCOM(printk("Write logo successful\n"));
                    hw_reset();
                }
            }
            break;

        case 0x04://FORMAT DOC
            FormatDocSafe();
            hw_reset();
            break;

        default:
            break;
    }

	elr_usb_close();
    msleep(1000);

    return 1;//Receive Succeeded
}

int _UsbDownloadImageEx(unsigned long uImageBase)
{
	int CmdCode, filesize, err = 0;//, retVal = 0;
	PRTCOM(printk("Elastos20 usb loader initialize ...\n"));

    InitElrUser();

    msleep(1000);

    CmdCode = WaitForCmd(0);

    if (-1 == CmdCode) {
        PRTCOM(printk("Unexpect Command Received!\n"));
        return 0;//
    }

    PRTCOM(printk("Expect Command Received!\n"));
    PRTCOM(printk("Start Receive file [0x%08x]...\n", uImageBase));
    err = ReceiveFile((char*)uImageBase, 0, &filesize);
    PRTCOM(printk("Kernel Image Receive "));

    if (err) {
        PRTCOM(printk("Failed!\n"));
        return 0;//Receive Failed
    }

    PRTCOM(printk("Succeeded!\n"));

	elr_usb_close();
    msleep(1000);

    return 1;//Receive Succeeded
}
